package com.bluexmicro.ota2.ui.screen.config.bxlink;

import android.bluetooth.BluetoothGattCharacteristic;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public abstract class BxLinkProtocol {

    private final static String TAG = "BxLinkProtocol";
    public final static byte[] bxl = new byte[]{
            0x42, (byte) 0x58, 0x4c
    };
    //todo 超长任务怎么办
    //这是一个分包的超时
    private final static long DEFAULT_MESSAGE_TIMEOUT = 5_000L;

    @NonNull
    private final Handler handler;

    public BxLinkProtocol() {
        HandlerThread handlerThread = new HandlerThread(TAG);
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
    }

    public abstract void onError(@NonNull String msg);

    public abstract void onReceivedBxLinkMessage(@NonNull BxLinkMessage message);

    private boolean collecting = false;

    public boolean isCollecting() {
        return collecting;
    }

    /**
     * header + content + foot
     * 要校验
     */
    ByteBuffer multipackBuffer;
    @Nullable
    BluetoothGattCharacteristic multipackGattCharacteristic;


    public void collectPartMessage(BluetoothGattCharacteristic characteristic, byte[] value) {
        byte[] header = null;
        if (value.length >= 8) {
            header = new byte[3];
            System.arraycopy(value, 0, header, 0, 3);
        }
        handler.removeCallbacks(multipackTimeoutRunnable);
        if (header != null && Arrays.equals(header, bxl)) {
            int contentLen = CommonUtils.toUInt16(value[7], value[6]);
            int dataLen = contentLen + 10; //header:8,foot:2
            multipackBuffer = ByteBuffer.allocate(dataLen).order(ByteOrder.LITTLE_ENDIAN);
            if (dataLen <= value.length) {
                // 只有一包数据
                multipackBuffer.put(value, 0, dataLen);
                handleCommon(characteristic, multipackBuffer);
                multipackBuffer = null;
            } else {
                collecting = true;
                // 多包数据的第一包,开启超时
                multipackBuffer.put(value);
                //开启新的，旧的分包任务如果未完成，就丢弃
                multipackGattCharacteristic = characteristic;
                handler.postDelayed(multipackTimeoutRunnable, DEFAULT_MESSAGE_TIMEOUT);
            }
        } else {
            if (multipackBuffer == null) {
                Log.e(TAG, "接收到了分包数据，但是此数据包已超时!");
                onError("接收到了分包数据，但是此数据包已超时!");
                return;
            }
            if (multipackGattCharacteristic != null && !multipackGattCharacteristic.getUuid().equals(characteristic.getUuid())) {
                onError("这个可能是上一个命令的分包数据，但是他已经过期了");
                return;
            }
            //分包数据
            int remainContentSize = multipackBuffer.capacity() - multipackBuffer.position();
            if (remainContentSize > value.length) {
                collecting = true;
                //还没收集完数据
                multipackBuffer.put(value);
                handler.postDelayed(multipackTimeoutRunnable, DEFAULT_MESSAGE_TIMEOUT);
            } else {
                // 最后一包
                multipackGattCharacteristic = null;
                multipackBuffer.put(value, 0, remainContentSize);
                handleCommon(characteristic, multipackBuffer);
                multipackBuffer = null;
            }
        }
    }

    private void handleCommon(BluetoothGattCharacteristic characteristic, ByteBuffer buffer) {
        BxLinkMessage message = new BxLinkMessage(buffer);
        message.setUuid(characteristic.getService().getUuid(), characteristic.getUuid());
        if (message.isCorrect()) {
            onReceivedBxLinkMessage(message);
        } else {
            Log.e(TAG, "bxLink消息校验失败");
            onError("bxLink消息校验失败");
        }
    }

    private final Runnable multipackTimeoutRunnable = () -> {
        collecting = false;
        onError("接收分包数据超时了");
        Log.e(TAG, "接收分包数据超时了");
    };

    ///////////////////////////////////////////////////////////////////////////
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 格式化 BxLink 协议的消息
     */
    private static byte[] buildBxLinkMessage(short cmdCode, byte[] content) {
        ByteBuffer buffer = ByteBuffer.allocate(content.length + 8).order(ByteOrder.LITTLE_ENDIAN);
        buffer.put(bxl)
                .put((byte) 0) //version
                .putShort(cmdCode) //code
                .putShort((short) content.length) // len
                .put(content);//content
        short crc = (short) CRC16Util.calcCrc16(buffer.array());

        ByteBuffer cmd = ByteBuffer.allocate(buffer.capacity() + 2).order(ByteOrder.LITTLE_ENDIAN);
        cmd.put(buffer.array()).putShort(crc);
        return cmd.array();
    }

    /**
     * 发送消息时用到
     * 分割 BxLink 消息
     */
    public static List<byte[]> splitBxLinkMessage(byte[] message, int mtuSize) {
        List<byte[]> results = new ArrayList<>();
        final int onePktSize = mtuSize - 3;
        int count = message.length / onePktSize;
        int remainLen = message.length % onePktSize;
        for (int i = 0; i < count; i++) {
            byte[] bytes = new byte[onePktSize];
            int srcPosition = i * onePktSize;
            System.arraycopy(message, srcPosition, bytes, 0, onePktSize);
            results.add(bytes);
        }
        if (remainLen > 0) {
            byte[] bytes = new byte[remainLen];
            int srcPosition = count * onePktSize;
            System.arraycopy(message, srcPosition, bytes, 0, remainLen);
            results.add(bytes);
        }
        return results;
    }

}